/*
 * ChangeHistory.java - Copyright Mar 14, 2012 Z Networks Group, Inc. All rights reserved.
 * Z Networks Group PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.andromeda.skadi.sandbox2;

import com.andromeda.skadi.data.DataChange;
import com.andromeda.skadi.property.EditableProperty;
import com.andromeda.skadi.property.Property;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 *
 * @author Dickinson Roman Arismendy Torres
 */
public class ChangeHistory<T extends Serializable> {

    private EditableProperty<Integer> cursor = new EditableProperty<Integer>(0);
    private EditableProperty<Boolean> canUndo = new EditableProperty<Boolean>(Boolean.FALSE);
    private EditableProperty<Boolean> canRedo = new EditableProperty<Boolean>(Boolean.FALSE);
    private EditableProperty<T> preview = new EditableProperty<T>(null);
    private EditableProperty<Integer> historySize = new EditableProperty<Integer>(100);
    private List<DataChange<T>> changes;
    private T originalValue;

    public ChangeHistory() {
        changes = new ArrayList<DataChange<T>>();
    }

    public void addChange(DataChange<T> change) {
        if (cursor.get() < changes.size()) {
            changes.subList(cursor.get() - 1, changes.size()).clear();
        }
        changes.add(change);
        cursor.set(cursor.get() + 1);
        updateUndoRedoActions();
        updatePreview();
    }

    public void undo() {
        if (canUndo.get()) {
            cursor.set(cursor.get() - 1);
            updateUndoRedoActions();
            updatePreview();
        }
    }

    private void updateUndoRedoActions() {
        canUndo.set(cursor.get() >= 0);
        canRedo.set(cursor.get() < changes.size());
    }

    public Property<Integer> cursor() {
        return cursor;
    }

    public DataChange<T> getLastChange() {
        return changes.get(cursor.get() - 1);
    }

    public void redo() {
        if (canRedo.get()) {
            cursor.set(cursor.get() + 1);
            updateUndoRedoActions();
            updatePreview();
        }
    }

    public List<DataChange<T>> getChanges() {
        if (cursor.get() > 0) {
            return Collections.unmodifiableList(changes.subList(0, cursor.get() - 1));
        }
        return Collections.EMPTY_LIST;
    }

    public Property<T> preview() {
        return preview;
    }

    private void updatePreview() {
        List<DataChange<T>> currentChanges = getChanges();
        T newValue = originalValue;
        for (DataChange<T> change : currentChanges) {
            newValue = change.apply(newValue);
        }
        preview.set(newValue);
    }

    public Property<Boolean> canUndo() {
        return canUndo;
    }

    public Property<Boolean> canRedo() {
        return canRedo;
    }

    public Property<Integer> historySize() {
        return historySize;
    }

    public void setOriginalValue(T originalValue) {
        this.originalValue = originalValue;
        updatePreview();
    }
}
